#pragma once

#include <cassert>
#include <cstdio>
#include <cstdlib>
#include <mutex>

#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

namespace kvalloc {

constexpr int SLAB_SIZE = 32 * 1024; // 32 KiB per slab

inline size_t file_size(const char *fname) {
    struct stat buffer; stat(fname, &buffer);
    return buffer.st_size;
}

inline bool folder_exist(const char *fname) {
    struct stat buffer;
    return stat(fname, &buffer) == 0 && S_ISDIR(buffer.st_mode);
}

inline bool file_exist(const char *fname) {
    struct stat buffer;
    return (stat(fname, &buffer) == 0);
}

// DataFile: manage data page allocation
class DataFile {
private:
    int fd_;
    size_t max_slabid_;
    std::mutex mu_;
    
public:
    DataFile(const char * fname) {
        fd_ = open(fname, O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
        if(fd_ < 3) {
            assert(fd_ < 3);
            perror("open file failed");
            exit(-1);
        }
        
        size_t fs = file_size(fname);
        max_slabid_ = fs > 0 ? fs / SLAB_SIZE : 1; // use the first page as a sentinal
    }
    
    ~DataFile() {close(fd_);}

    // allocate serveral pages
    size_t AllocPages(int num) {
        mu_.lock();
            int old_pid = max_slabid_;
            max_slabid_ += num;
            ftruncate(fd_, max_slabid_ * SLAB_SIZE);
        mu_.unlock();
        return old_pid;
    }

    void Read(uint64_t offset, char * buf, uint64_t size) {
        pread(fd_, buf, size, offset);
    }

    // get the file discriptor
    int GetFD() const {
        return fd_;
    }

    size_t GetMaxPageID() const {
        return max_slabid_;
    }
};

} // namespace kvalloc